/*
 * Copyright 2012-2021 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.boot;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.function.Consumer;

import org.apache.commons.logging.Log;

import org.springframework.boot.context.event.EventPublishingRunListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.metrics.ApplicationStartup;
import org.springframework.core.metrics.StartupStep;
import org.springframework.util.ReflectionUtils;

import static org.eclipse.jetty.util.component.LifeCycle.start;

/**
 * 主要作用是在 Spring Boot 应用程序的生命周期中发布各种事件。
 * A collection of {@link SpringApplicationRunListener}.
 *
 * @author Phillip Webb
 * @author Andy Wilkinson
 * @author Chris Bono
 *
 * 当前类使用了观察者模式，这是观察者模式中被观察的类，也被称为主题类。
 * 因为观察者模式也被称为：发布订阅模式（主题类就是用来发布消息的）
 * 观察者就是用来接受消息的。
 */
class SpringApplicationRunListeners {

	private final Log log;

	// 这个类，就是观察者。
	private final List<SpringApplicationRunListener> listeners;
	// 这就是主题类中的状态
	private final ApplicationStartup applicationStartup;

	SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners, ApplicationStartup applicationStartup) {
		this.log = log;
		this.listeners = new ArrayList<>(listeners);
		this.applicationStartup = applicationStartup;
	}

	void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
		// 这里是我修改后的代码：
		// 原来用 Lambda 表达式,表示函数式接口的实例对象，我改成了用普通的方式就是：
//		WqmStartingListener wqmStartingListener = new WqmStartingListener(bootstrapContext);
//		WqmMainApplicationClassTagSetter wqmMainApplicationClassTagSetter = new WqmMainApplicationClassTagSetter(mainApplicationClass);
//		// 开始监听
//		doWithListeners("spring.boot.application.starting", wqmStartingListener, wqmMainApplicationClassTagSetter);

		/**
		 * 下面是源代码：
		 * {@link  EventPublishingRunListener#starting(ConfigurableBootstrapContext)}
		 */
		doWithListeners("spring.boot.application.starting",
				(listener) -> listener.starting(bootstrapContext),
				(step) -> {
					if (mainApplicationClass != null) {
						step.tag("mainApplicationClass", mainApplicationClass.getName());
					}
				});
	}

	void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
		doWithListeners("spring.boot.application.environment-prepared", (listener) -> listener.environmentPrepared(bootstrapContext, environment));
	}

	void contextPrepared(ConfigurableApplicationContext context) {
		doWithListeners("spring.boot.application.context-prepared", (listener) -> listener.contextPrepared(context));
	}

	void contextLoaded(ConfigurableApplicationContext context) {
		doWithListeners("spring.boot.application.context-loaded", (listener) -> listener.contextLoaded(context));
	}

	void started(ConfigurableApplicationContext context, Duration timeTaken) {
		doWithListeners("spring.boot.application.started", (listener) -> listener.started(context, timeTaken));
	}

	void ready(ConfigurableApplicationContext context, Duration timeTaken) {
		doWithListeners("spring.boot.application.ready", (listener) -> listener.ready(context, timeTaken));
	}

	void failed(ConfigurableApplicationContext context, Throwable exception) {
		doWithListeners("spring.boot.application.failed",
				(listener) -> callFailedListener(listener, context, exception), (step) -> {
					step.tag("exception", exception.getClass().toString());
					step.tag("message", exception.getMessage());
				});
	}

	private void callFailedListener(SpringApplicationRunListener listener, ConfigurableApplicationContext context,
			Throwable exception) {
		try {
			listener.failed(context, exception);
		}
		catch (Throwable ex) {
			if (exception == null) {
				ReflectionUtils.rethrowRuntimeException(ex);
			}
			if (this.log.isDebugEnabled()) {
				this.log.error("Error handling failed", ex);
			}
			else {
				String message = ex.getMessage();
				message = (message != null) ? message : "no error message";
				this.log.warn("Error handling failed (" + message + ")");
			}
		}
	}

	private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction) {
		doWithListeners(stepName, listenerAction, null);
	}

	/**
	 * Consumer是Java标准库中的 {@link  java.util.function} 包下的接口类。
	 * Consumer 接口定义了一个接受一个参数并且不返回结果的操作，通常用于表示消费者或处理器。
	 * <p>
	 * 下面两个参数：
	 * Consumer<SpringApplicationRunListener> listenerAction
	 * Consumer<StartupStep> stepAction
	 * 都表示在传入参数时，需要传入函数式接口的实例 （而Lambda表达式可以比较方便地创建这些函数式接口的实例）
	 * 在传入这些参数之前，都需要执行对应的函数方法。
	 */
	private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction, Consumer<StartupStep> stepAction) {
		ApplicationStartup applicationStartup1 = this.applicationStartup;
		StartupStep step = applicationStartup1.start(stepName);

		this.listeners.forEach(listenerAction);

		if (stepAction != null) {
			stepAction.accept(step);
		}
		step.end();
	}


	/**
	 * 不用Lambda 表达式完成函数式接口实例，就自己实现接口然后单独实现。
	 * 实现Consumer<StartupStep>接口的类
	 */
	class WqmMainApplicationClassTagSetter implements Consumer<StartupStep> {
		private Class<?> mainApplicationClass;

		public WqmMainApplicationClassTagSetter(Class<?> mainApplicationClass) {
			this.mainApplicationClass = mainApplicationClass;
		}

		@Override
		public void accept(StartupStep step) {
			if (mainApplicationClass != null) {
				step.tag("mainApplicationClass", mainApplicationClass.getName());
			}
		}
	}

	/**
	 *  不用Lambda 表达式完成函数式接口实例，就自己实现接口然后单独实现。
	 *  实现Consumer<SpringApplicationRunListener>接口的类
	 */
	class WqmStartingListener implements Consumer<SpringApplicationRunListener> {
		private ConfigurableBootstrapContext bootstrapContext;

		public WqmStartingListener(ConfigurableBootstrapContext bootstrapContext) {
			this.bootstrapContext = bootstrapContext;
		}

		@Override
		public void accept(SpringApplicationRunListener listener) {
			/**
			 * {@link  EventPublishingRunListener#starting(ConfigurableBootstrapContext)}
			 */
			listener.starting(bootstrapContext);
		}
	}



}
